﻿using AbpDemo.AdminRoles;
using AbpDemo.Admins;
using AbpDemo.Articles;
using AbpDemo.Authors;
using AbpDemo.Menus;
using AbpDemo.RoleMenus;
using AbpDemo.Roles;
using AbpDemo.Rooms;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Reflection;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;

namespace AbpDemo.EntityFrameworkCore;

[ConnectionStringName("Default")]
public class AbpDemoDbContext :
    AbpDbContext<AbpDemoDbContext>
{
    private readonly IHttpContextAccessor httpContext;

    public DbSet<Admin> Admins { get; set; }
    public DbSet<Menu> Menus { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<AdminRole> AdminRoles { get; set; }
    public DbSet<RoleMenu> RoleMenus { get; set; }
    public DbSet<Author> Authors { get; set; }
    public DbSet<Room> Rooms { get; set; }
    public DbSet<Article> Articles { get; set; }

    public AbpDemoDbContext(DbContextOptions<AbpDemoDbContext> options, IHttpContextAccessor httpContext)
        : base(options)
    {
        this.httpContext = httpContext;
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        /* Include modules to your migration db context */

        builder.ConfigureBackgroundJobs();
        builder.ConfigureAuditLogging();

        /* Configure your own tables/entities inside here */

        builder.Entity<Admin>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Admin), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.UserName).IsRequired().HasMaxLength(50);
            b.Property(m => m.Password).IsRequired().HasMaxLength(50);
        });

        builder.Entity<Role>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Role), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.RoleName).IsRequired().HasMaxLength(50);
            b.Property(m => m.Remark).HasMaxLength(500);
        });

        builder.Entity<Menu>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Menu), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.ApiPath).HasMaxLength(50);
            b.Property(m => m.ComponentPath).HasMaxLength(50);
            b.Property(m => m.Icon).HasMaxLength(50);
            b.Property(m => m.MenuName).IsRequired().HasMaxLength(50);
            b.Property(m => m.Redirect).HasMaxLength(100);
            b.Property(m => m.RouteName).HasMaxLength(50);
            b.Property(m => m.RoutePath).HasMaxLength(50);
            b.Property(m => m.Permission).HasMaxLength(50);
            b.Property(m => m.HttpMethod).HasMaxLength(50);
        });

        builder.Entity<AdminRole>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(AdminRole), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.HasOne<Admin>().WithMany().HasForeignKey(m => m.AdminId);
            b.HasOne<Role>().WithMany().HasForeignKey(m => m.RoleId);
        });

        builder.Entity<RoleMenu>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(RoleMenu), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.HasOne<Menu>().WithMany().HasForeignKey(m => m.MenuId);
            b.HasOne<Role>().WithMany().HasForeignKey(m => m.RoleId);
        });

        builder.Entity<Author>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Author), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.AuthorName).IsRequired().HasMaxLength(50);
            b.Property(m => m.Remark).HasMaxLength(500);
        });

        builder.Entity<Room>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Room), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.RoomCode).IsRequired().HasMaxLength(50);
        });

        builder.Entity<Article>(b =>
        {
            b.ToTable(AbpDemoConsts.DbTablePrefix + nameof(Article), AbpDemoConsts.DbSchema);
            b.ConfigureByConvention(); //auto configure for the base class props

            b.Property(m => m.Title).IsRequired().HasMaxLength(50);
            b.Property(m => m.Content).IsRequired().HasMaxLength(50);
        });
    }

    public override void Initialize(AbpEfCoreDbContextInitializationContext initializationContext)
    {
        ChangeTracker.StateChanged += ChangeTracker_StateChanged1;
        ChangeTracker.Tracked += ChangeTracker_Tracked1;
    }

    private void ChangeTracker_Tracked1(object sender, Microsoft.EntityFrameworkCore.ChangeTracking.EntityTrackedEventArgs e)
    {
        if (httpContext.HttpContext.User.Identity.IsAuthenticated)
        {
            var CurrentUserId = httpContext.HttpContext.User.Claims.First(m => m.Type == JwtClaimTypes.Id).Value;
            var CurrentUserName = httpContext.HttpContext.User.Identity.Name;

            if (!string.IsNullOrEmpty(CurrentUserName) && !string.IsNullOrEmpty(CurrentUserName))
            {
                object o = e.Entry.Entity;
                PropertyInfo[] props = o.GetType().GetProperties();
                if (e.Entry.State == EntityState.Added)
                {
                    if (props.Select(m => m.Name).Contains("CreatorId") && props.Select(m => m.Name).Contains("CreatorName"))
                    {
                        props.First(m => m.Name == "CreatorId").SetValue(o, Guid.Parse(CurrentUserId));
                        props.First(m => m.Name == "CreatorName").SetValue(o, CurrentUserName);
                        props.First(m => m.Name == "CreationTime").SetValue(o, DateTime.Now);
                    }
                }
            }
        }
    }

    private void ChangeTracker_StateChanged1(object sender, Microsoft.EntityFrameworkCore.ChangeTracking.EntityStateChangedEventArgs e)
    {
        var prop1 = e.Entry.Property;
        var prop2 = e.Entry.Properties;
        if (httpContext.HttpContext.User.Identity.IsAuthenticated)
        {
            var CurrentUserId = httpContext.HttpContext.User.Claims.First(m => m.Type == JwtClaimTypes.Id).Value;
            var CurrentUserName = httpContext.HttpContext.User.Identity.Name;

            if (!string.IsNullOrEmpty(CurrentUserName) && !string.IsNullOrEmpty(CurrentUserName))
            {
                object o = e.Entry.Entity;
                
                PropertyInfo[] props = o.GetType().GetProperties();
                if (e.Entry.State == EntityState.Modified)
                {                    
                    if (e.Entry.Entity is ISoftDelete && e.Entry.Entity.As<ISoftDelete>().IsDeleted)
                    {
                        props.First(m => m.Name == "DeleterId").SetValue(o, Guid.Parse(CurrentUserId));
                        props.First(m => m.Name == "DeleterName").SetValue(o, CurrentUserName);
                    }
                    else
                    {
                        if (props.Select(m => m.Name).Contains("LastModifierId") && props.Select(m => m.Name).Contains("LastModifierName"))
                        {
                            props.First(m => m.Name == "LastModifierId").SetValue(o, Guid.Parse(CurrentUserId));
                            props.First(m => m.Name == "LastModifierName").SetValue(o, CurrentUserName);
                            props.First(m => m.Name == "LastModificationTime").SetValue(o, DateTime.Now);
                        }
                    }
                }
                if(e.Entry.State == EntityState.Deleted)
                {
                    if (e.Entry.Entity is ISoftDelete)
                    { 
                        e.Entry.Reload();
                        props.First(m => m.Name == "DeleterId").SetValue(o, Guid.Parse(CurrentUserId));
                        props.First(m => m.Name == "DeleterName").SetValue(o, CurrentUserName);                    
                        props.First(m => m.Name == "DeletionTime").SetValue(o, DateTime.Now);                    
                        props.First(m => m.Name == "IsDeleted").SetValue(o, true);
                    }
                }
            }
        }
    }
}
